package etnorservice.etnor_service.utils;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: cc
 * @Date: 2024/08/29/9:37
 **/


import java.util.concurrent.atomic.AtomicLong;

public class SnowflakeIdGenerator {

    private static final long WORKER_ID_BITS = 5L;
    private static final long DATA_CENTER_ID_BITS = 5L;
    private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
    private static final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);
    private static final long SEQUENCE_BITS = 12L;

    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
    private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
    private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;
    private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);

    private static AtomicLong lastTimestamp = new AtomicLong(-1L);
    private static AtomicLong sequence = new AtomicLong(0L);

    private static long workerId;
    private static long dataCenterId;

    private static volatile long twepoch = 1288834974657L;

    public SnowflakeIdGenerator(long workerId, long dataCenterId) {
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", MAX_WORKER_ID));
        }
        if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) {
            throw new IllegalArgumentException(String.format("dataCenter Id can't be greater than %d or less than 0", MAX_DATA_CENTER_ID));
        }
        SnowflakeIdGenerator.workerId = workerId;
        SnowflakeIdGenerator.dataCenterId = dataCenterId;
    }

    /**
     * 生成一个全局唯一的ID
     *
     * @return 全局唯一的长整型ID
     */
    public synchronized long nextId() {
        long timestamp = timeGen();

        if (timestamp < lastTimestamp.get()) {
            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp.get() - timestamp));
        }

        if (lastTimestamp.get() == timestamp) {
            sequence.set((sequence.get() + 1) & SEQUENCE_MASK);
            if (sequence.get() == 0) {
                timestamp = tilNextMillis(lastTimestamp.get());
            }
        } else {
            sequence.set(0L);
        }

        lastTimestamp.set(timestamp);

        return ((timestamp - twepoch) << TIMESTAMP_LEFT_SHIFT) |
                (dataCenterId << DATA_CENTER_ID_SHIFT) |
                (workerId << WORKER_ID_SHIFT) |
                (sequence.get() & SEQUENCE_MASK);
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }


}
